/**
 * Copyright (C) 2013-2014 the original author or authors.
 */
package com.foreveross.bsl.system.application.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.inject.Named;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.util.Assert;

import com.foreveross.bsl.common.utils.bean.Collections3;
import com.foreveross.bsl.common.utils.mapper.BeanMapper;
import com.foreveross.bsl.mongodb.domain.repository.EntityRepository;
import com.foreveross.bsl.system.application.AppCloudLinkService;
import com.foreveross.bsl.system.application.SystemModuleException;
import com.foreveross.bsl.system.application.vo.CloudFunctionVo;
import com.foreveross.bsl.system.model.domain.AppCloudMappingEntry;
import com.foreveross.bsl.system.model.domain.CloudFunction;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * xxx
 * 
 * @author Wangyi
 * @version v1.0
 * 
 * @date 2013-7-17
 * 
 */
@Named("appCloudLinkService")
public class AppCloudLinkServiceImpl implements AppCloudLinkService {

	@Autowired
	private MongoTemplate mongoTemplate;

	private final EntityRepository getAppCloudMappingRepository() {
		return new AppCloudMappingEntry().getEntityRepository();
	}

	private EntityRepository getCloudFunctionRepository() {
		return new CloudFunction().getEntityRepository();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#link
	 * (java.lang.String, java.lang.String)
	 */
	@Override
	public void link(String appId, String cloudId, String userId) {
		Assert.notNull(appId);
		Assert.notNull(cloudId);
		Assert.notNull(userId);
		List<AppCloudMappingEntry> list = getAppCloudMappingRepository()
				.findListByQuery(
						Query.query(Criteria.where("appId").is(appId)
								.and("cloudId").is(cloudId)));
		if (list == null || list.isEmpty()) {
			AppCloudMappingEntry entry = new AppCloudMappingEntry();
			entry.setAppId(appId);
			entry.setBelongUserId(userId);
			entry.setCreateTime(new Date());
			entry.setCloudId(cloudId);
			getAppCloudMappingRepository().save(entry);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#
	 * linkByCode(java.lang.String, java.lang.String)
	 */
	@Override
	public void linkByCode(String appId, String cloudCode, String userId) {
		Assert.notNull(appId);
		Assert.notNull(cloudCode);
		Assert.notNull(userId);
		List<CloudFunction> list = getCloudFunctionRepository()
				.findListByQuery(
						Query.query(Criteria.where("code").is(cloudCode)));
		if (list == null || list.isEmpty()) {
			throw new SystemModuleException("不存在代号为：" + cloudCode + "的云服务");
		}
		CloudFunction cf = list.iterator().next();
		AppCloudMappingEntry entry = new AppCloudMappingEntry();
		entry.setAppId(appId);
		entry.setBelongUserId(userId);
		entry.setCreateTime(new Date());
		entry.setCloudId(cf.getId());
		getAppCloudMappingRepository().save(entry);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#unlink
	 * (java.lang.String, java.lang.String)
	 */
	@Override
	public void unlink(String appId, String cloudId) {
		Assert.notNull(appId);
		Assert.notNull(cloudId);
		List<AppCloudMappingEntry> list = getAppCloudMappingRepository()
				.findListByQuery(
						Query.query(Criteria.where("appId").is(appId)
								.and("cloudId").is(cloudId)));
		if (list != null && !list.isEmpty()) {
			getAppCloudMappingRepository().delete(list.iterator().next());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#
	 * unlinkByCode(java.lang.String, java.lang.String)
	 */
	@Override
	public void unlinkByCode(String appId, String cloudCode) {
		Assert.notNull(appId);
		Assert.notNull(cloudCode);
		List<CloudFunction> list = getCloudFunctionRepository()
				.findListByQuery(
						Query.query(Criteria.where("code").is(cloudCode)));
		if (list != null && !list.isEmpty()) {
			this.unlink(appId, list.iterator().next().getId());
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#
	 * getLinkedCount(java.lang.String, java.lang.String)
	 */
	@Override
	public int getLinkedCount(String cloudId, String userId) {
		Assert.notNull(userId, "userId param can not be null");
		Assert.notNull(cloudId, "cloudId param can not be null");
		Query query = Query.query(Criteria.where("cloudId").is(cloudId)
				.and("belongUserId").is(userId));
		List<AppCloudMappingEntry> entrys = getAppCloudMappingRepository()
				.findListByQuery(query);
		List<String> appIds = new ArrayList<String>(entrys.size());
		for (AppCloudMappingEntry entry : entrys) {
			appIds.add(entry.getAppId());
		}
		return 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.foreveross.bsl.system.application.cloudfunc.AppCloudLinkService#
	 * getLinkedCounts(java.util.Set, java.lang.String)
	 */
	@Override
	public Map<String, Integer> getLinkedCounts(String userId) {
		Assert.notNull(userId, "userId param can not be null");
		Criteria cri = Criteria.where("belongUserId").is(userId);
		GroupBy groupBy = GroupBy.key("cloudId");
		groupBy.initialDocument("{appCount:0}");
		groupBy.reduceFunction("function(cur,result){result.appCount++}");
		GroupByResults<AppCountPerCloud> result = this.mongoTemplate.group(cri,
				"appCloudMappingEntry", groupBy, AppCountPerCloud.class);
		Map<String, Integer> ret = Maps.newHashMap();
		Iterator<AppCountPerCloud> it = result.iterator();
		while (it.hasNext()) {
			AppCountPerCloud t = it.next();
			ret.put(t.getCloudId(), t.getAppCount());
		}
		return ret;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.foreveross.bsl.system.application.AppCloudLinkService#getLinkedAppIds
	 * (java.lang.String, java.lang.String)
	 */
	@Override
	public List<String> getLinkedAppIds(String userId, String cloudId) {
		Assert.notNull(userId, "userId param can not be null");
		Assert.notNull(cloudId, "cloudId param can not be null");
		Query query = Query.query(Criteria.where("cloudId").is(cloudId)
				.and("belongUserId").is(userId));
		List<AppCloudMappingEntry> entrys = getAppCloudMappingRepository()
				.findListByQuery(query);
		List<String> appIds = new ArrayList<String>(entrys.size());
		for (AppCloudMappingEntry entry : entrys) {
			appIds.add(entry.getAppId());
		}
		return appIds;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.foreveross.bsl.system.application.AppCloudLinkService#
	 * getLinkedCloudFunctions(java.lang.String, java.lang.String)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<CloudFunctionVo> getLinkedCloudFunctions(String userId,
			String appId) {
		Assert.notNull(userId);
		Assert.notNull(appId);
		Query query = Query.query(Criteria.where("appId").is(appId)
				.and("belongUserId").is(userId));
		List<AppCloudMappingEntry> entrys = getAppCloudMappingRepository()
				.findListByQuery(query);
		List<String> cloudIds = Collections3.extractToList(entrys, "cloudId");
		List<CloudFunction> clouds = Lists
				.newArrayList(getCloudFunctionRepository().findAll(cloudIds));
		return BeanMapper.mapList(clouds, CloudFunctionVo.class);
	}

	@Override
	public void delLinkedCloudByApps(String appId, String userId) {
		Assert.notNull(userId);
		Assert.notNull(appId);
		Query query = Query.query(Criteria.where("appId").is(appId)
				.and("belongUserId").is(userId));
		List<AppCloudMappingEntry> entrys = getAppCloudMappingRepository()
				.findListByQuery(query);
		if (null != entrys && !entrys.isEmpty()) {
			for (AppCloudMappingEntry appCloudMappingEntry : entrys) {
				getAppCloudMappingRepository().delete(appCloudMappingEntry);
			}
		}
	}

	class AppCountPerCloud {
		private String cloudId;
		private int appCount;

		/**
		 * @return the cloudId
		 */
		public String getCloudId() {
			return cloudId;
		}

		/**
		 * @param cloudId
		 *            the cloudId to set
		 */
		public void setCloudId(String cloudId) {
			this.cloudId = cloudId;
		}

		/**
		 * @return the appCount
		 */
		public int getAppCount() {
			return appCount;
		}

		/**
		 * @param appCount
		 *            the appCount to set
		 */
		public void setAppCount(int appCount) {
			this.appCount = appCount;
		}

	}
}
